# This file is part of jukuMagic.
# (c) 2010 Perttu Ahola <celeron55@gmail.com>
##############################################################################
# This is the configuration file of jukuMagic. As you can see, it's a plain
# python script.
# The API is documented at doc/python_api_funcs.txt
# Not all of its usefulness is used here. Check that file if you need
# something.

import juku
import sys
import operator
import os
import re

class Error(Exception):
	"""Base class for exceptions in this module."""
	pass


class fp_reader:
	def read(self, filepath):
		# The name taken from the file name
		namefile = 'noname'
		m = re.search(r'.*/([^/]*).fp', filepath)
		if m != None:
			namefile = m.group(1)
		return # TODO
		print 'fp_reader('+filepath+'): namefile='+namefile
		file = open(filepath)
		content = file.read()
		file.close()
#		read_content(content)
	
	# Comments should have been stripped out before this
	def read_content(self, content):
		while content != '':
			m = re.search(r'\s*([A-Z]\w*)\s*\(', content)
			if m == None:
				# We have an invalid file here
				print 'invalid file'
				return

def readpcb_dir(s):
	for name in os.listdir(s):
		path = os.path.join(s,name)
		if os.path.isdir(path):
			readpcb_dir(path)
			continue
		#print path
		if path[len(path)-3:len(path)] != '.fp':
			continue
		

		reader = fp_reader()
		reader.read(path)

		#footprint = None
		
		#file = open(path)

		#content = file.read();

		# Replace obvious comments with nothing
		#content = re.sub(r'^\s*#.*\n', r'', content)
"""
		read_content(content)

		for line in file:
			m = re.search(r'Element\((0x..)\s+"([\w ]*)"\s+"([\w ]*)"\s+"([\w ]*)"\s+(\d+)\s+(\d+)\s+(.)\s+(\d*)\s+(0x..)\)', line)
			if m != None:
				nflags = m.group(1)
				desc = m.group(2)
				name = m.group(3)
				tx = m.group(4)
				ty = m.group(5)
				tdir = m.group(6)
				tscale = m.group(7)
				tnflags = m.group(8)
				print namefile+' = '+path
				print 'element: desc="'+desc+'" name="'+name+'"'
				#print nflags+' '+desc+' '+name+' '+tx+' '+ty+' '+tdir+' '+tscale
				footprint = juku.footprint()
				footprint.name = namefile
				footprint.subname = desc
				continue;

			#sys.stdout.write('didn\'t match: '+line)
"""

	
print "colour_selected is "+juku.get_setting("colour_selected")
newcolor = "rgb(50,200,50)"
print "setting it to "+newcolor
juku.set_setting("colour_selected", newcolor);

juku.library_add_directory(os.getcwd()+'/library')
juku.library_add_directory(os.path.expanduser('~/jukumagic/library'))
#juku.library_add_directory(os.path.expanduser('~/gaf/pcb-elements'))
#juku.library_add_directory('/usr/share/pcb/pcblib-newlib')

footprint = juku.footprint()
footprint.name = 'test'
footprint.subname = 'subtest'
footprint.side = 'top'
footprint.add_pin('1','1','through_hole',(0,0),True)
footprint.add_pin('2','2','through_hole',(100,0),False)
footprint.add_graphic_rect((-50,-50),(200,100))
juku.library_add_footprint(footprint, 'pytest')

readpcb_dir('/usr/share/pcb/pcblib-newlib')


juku.library_sort()


#=== File menu ===

def file_new(id):
	juku.file_new('')
juku.add_menu_command("File", "New", file_new)

def file_open(id):
	(ok, filepath) = juku.open_file_dialog("Load a jukuMagic2 file", "jukuMagic files (*.juku)|*.juku|All files (*.*)|*.*")
	if ok:
		juku.file_open(filepath)
juku.add_menu_command("File", "Open", file_open)

def file_save(id):
	filepath = juku.get_setting("m_current_file_path")
	if filepath != '':
		juku.file_save(filepath)
	else:
		(ok, filepath) = juku.save_file_dialog("Write a jukuMagic2 file", "", "jukuMagic files (*.juku)|*.juku|All files (*.*)|*.*", False)
		if ok:
			juku.file_save(filepath)
id = juku.add_menu_command("File", "Save (ctrl+s)", file_save)
juku.add_accelerator('ctrl', 's', id)

def file_saveas(id):
	(ok, filepath) = juku.save_file_dialog("Write a jukuMagic2 file", "", "jukuMagic files (*.juku)|*.juku|All files (*.*)|*.*", True)
	if ok:
		juku.file_save(filepath)
juku.add_menu_command("File", "Save As", file_saveas)

def file_close(id):
	juku.file_close()
juku.add_menu_command("File", "Close", file_close)

def file_settings(id):
	juku.file_settings()
juku.add_menu_command("File", "Settings", file_settings)

def file_import_spice(id):
	(ok, filepath) = juku.open_file_dialog("Load a spice netlist file", "All files (*.*)|*.*")
	if ok:
		juku.load_netlist_spice(filepath)
juku.add_menu_command("File|Import", "Spice netlist", file_import_spice)

def file_import_partslist3(id):
	(ok, filepath) = juku.open_file_dialog("Load a gnetlist partslist3 file", "All files (*.*)|*.*")
	if ok:
		juku.load_partslist_gnetlist(filepath)
juku.add_menu_command("File|Import", "gnetlist partslist3", file_import_partslist3)

def export_cut_csv(filepath):
	file = open(filepath, 'w')
	cuts = juku.get_cuts()
	for cut in cuts:
		(is_narrow, x, y) = cut
		file.write(str(is_narrow)+", "+str(x)+", "+str(y)+"\n")
	file.close()

def file_export_cut_csv(id):
	filepath = juku.get_setting("m_current_file_path")
	filepath += "_cuts.csv"
	(ok, filepath) = juku.save_file_dialog("Write a Cut CSV file", filepath, "All files (*.*)|*.*", True)
	if ok:
		export_cut_csv(filepath)
		#juku.write_cut_csv(filepath)
juku.add_menu_command("File|Export", "Cut CSV", file_export_cut_csv)

def get_minimal_traces(cuts, holes, y):
	traces = []
	x = 0
	while True:
		trace_start = -1
		trace_end = -1
		#trace start = first hole
		while True:
			try: (i for i in holes if i[0] == x and i[1] == y).next()
			except StopIteration:
				x += 1
				if x >= juku.get_board_w():
					return traces
				continue
			break
		trace_start = x
		#find next cut
		while True:
			try: (i for i in holes if i[0] == x and i[1] == y).next()
			except StopIteration:
				pass
			else:
				trace_end = x
			try: (i for i in cuts if i[1] == x and i[2] == y).next()
			except StopIteration:
				x += 1
				if x >= juku.get_board_w():
					if trace_end != -1:
						traces.append((trace_start, trace_end))
					return traces
				continue
			break
		traces.append((trace_start, trace_end))
		x += 1
		if x >= juku.get_board_w():
			return traces

safe_height = 0.1
rapid_height = 0.03
cut_depth = 0.01
drill_depth = 1./16
feedrate = 60
feedrate_z = 20

class gcode_exporter:
	def out(self, text):
		self.file.write(text)
	def comment(self, text):
		self.out('('+text+')\n')
	def inches(self):
		self.out('G20\n')
	def millimeters(self):
		self.out('G21\n')
	def move(self, rapid, x=None, y=None, z=None):
		if rapid:
			self.out('G0')
		else:
			self.out('G1')
			if z == None:
				self.out(' F'+str(feedrate))
			else:
				self.out(' F'+str(feedrate_z))
		if x != None:
			self.out(' X'+str(x))
		if y != None:
			self.out(' Y'+str(y))
		if z != None:
			self.out(' Z'+str(z))
			self.z = z
		self.out('\n')
	def rapid(self, x=None, y=None, z=None):
		self.move(True, x, y, z)
	def cut(self, x=None, y=None, z=None):
		self.move(False, x, y, z)
	def drill_z(self):
		if self.z == None:
			self.cut(z = -drill_depth)
			return
		if self.z > rapid_height:
			self.rapid(z = rapid_height)
		self.cut(z = -drill_depth)
	def lower_z(self):
		if self.z == None:
			self.cut(z = -cut_depth)
			return
		if self.z > rapid_height:
			self.rapid(z = rapid_height)
		self.cut(z = -cut_depth)
	def raise_z(self):
		if self.z == None:
			self.cut(z = safe_height)
			return
		if self.z < rapid_height:
			self.cut(z = rapid_height)
		self.rapid(z = safe_height)
	def __init__(self, file=sys.stdout):
		self.file = file
		self.z = None
		#self.out('G94 G97\n')
		self.inches()
	def end(self):
		self.out('M2\n')

def export_gcode(filepath):
	cuts = juku.get_cuts()
	holes = juku.get_holes()
	file = open(filepath, 'w')
	gcode = gcode_exporter(file)
	gcode.raise_z()
	for y in range(0, juku.get_board_h()):
		traces = get_minimal_traces(cuts, holes, y)
		for trace in traces:
			gcode.comment('row '+str(y)+': '+str(trace[0])+'-'+str(trace[1]))
			gcode.rapid(trace[0]/10., y/10.)
			gcode.lower_z()
			gcode.cut(trace[0]/10., y/10.+0.1)
			gcode.cut(trace[1]/10.+0.1, y/10.+0.1)
			gcode.cut(trace[1]/10.+0.1, y/10.)
			gcode.cut(trace[0]/10., y/10.)
			gcode.raise_z()
	#sort holes by x
	holes = sorted(holes, key=operator.itemgetter(0))
	for i in holes:
		gcode.comment('hole at '+str(i[0])+','+str(i[1]))
		gcode.rapid(i[0]/10.+0.05, i[1]/10.+0.05)
		gcode.drill_z()
		gcode.raise_z()
	gcode.end()
	file.close()

def file_export_gcode(id):
	filepath = juku.get_setting("m_current_file_path")
	filepath += ".ngc"
	(ok, filepath) = juku.save_file_dialog("Export G-Code", filepath, "All files (*.*)|*.*", True)
	if ok:
		export_gcode(filepath)
juku.add_menu_command("File|Export", "G-Code", file_export_gcode)

def file_exit(id):
	juku.exit()
juku.add_menu_command("File", "Exit", file_exit)

#=== View menu ===

def show_refdes(id):
	juku.set_setting("m_component_show_infotype", "COMPONENT_INFO_REFDES")
	juku.refresh_board()
juku.add_menu_command("View", "Show Refdes", show_refdes)

def show_value(id):
	juku.set_setting("m_component_show_infotype", "COMPONENT_INFO_VALUE")
	juku.refresh_board()
juku.add_menu_command("View", "Show Value", show_value)

def show_none(id):
	juku.set_setting("m_component_show_infotype", "COMPONENT_INFO_NONE")
	juku.refresh_board()
juku.add_menu_command("View", "Show None", show_none)

"""
def check_test(id, state):
	print "state="+str(state)
id = juku.add_menu_check("View", "test", check_test, 0)

def check_test2(id, state):
	print "state2="+str(state)
juku.add_menu_check("View", "test", check_test2, 1)
"""

#=== Edit menu ===

def edit_delete(id):
	juku.delete_selected()
id = juku.add_menu_command("Edit", "Delete (ctrl+d, delete)", edit_delete)
juku.add_accelerator('none', 'WXK_DELETE', id)
juku.add_accelerator('ctrl', 'd', id)

def edit_rotate_90(id):
	juku.selection_rotate_90()
id = juku.add_menu_command("Edit", "Rotate (ctrl+r)", edit_rotate_90)
juku.add_accelerator('ctrl', 'r', id)

#=== Mode menu ===

def mode_select(id):
	juku.set_mouse_mode('select')
id = juku.add_menu_command("Mode", "Select (alt+s, escape)", mode_select)
juku.add_accelerator('alt', 's', id)
juku.add_accelerator('none', 'WXK_ESCAPE', id)

def mode_delete(id):
	juku.set_mouse_mode('delete')
id = juku.add_menu_command("Mode", "Delete", mode_delete)

def mode_move_component_head(id):
	juku.set_mouse_mode('move_component_head')
id = juku.add_menu_command("Mode", "Move Component Head (alt+m)", mode_move_component_head)
juku.add_accelerator('alt', 'm', id)

def mode_add_wire(id):
	juku.set_mouse_mode('add_wire')
id = juku.add_menu_command("Mode", "Add Wire (alt+w)", mode_add_wire)
juku.add_accelerator('alt', 'w', id)

def mode_add_wire_bottom(id):
	juku.set_mouse_mode('add_wire_bottom')
id = juku.add_menu_command("Mode", "Add Wire (bottom) (alt+b)", mode_add_wire_bottom)
juku.add_accelerator('alt', 'b', id)

def mode_add_cut(id):
	juku.set_mouse_mode('add_cut')
id = juku.add_menu_command("Mode", "Cut (alt+x)", mode_add_cut)
juku.add_accelerator('alt', 'x', id)

def mode_add_narrowcut(id):
	juku.set_mouse_mode('add_narrowcut')
id = juku.add_menu_command("Mode", "Cut Narrow (alt+z)", mode_add_narrowcut)
juku.add_accelerator('alt', 'z', id)

def mode_add_resistor(id):
	juku.set_mouse_mode('add_resistor')
id = juku.add_menu_command("Mode", "Add Resistor (alt+r)", mode_add_resistor)
juku.add_accelerator('alt', 'r', id)

def mode_add_diode(id):
	juku.set_mouse_mode('add_diode')
id = juku.add_menu_command("Mode", "Add Diode (alt+d)", mode_add_diode)
juku.add_accelerator('alt', 'd', id)

def mode_add_ceramic(id):
	juku.set_mouse_mode('add_ceramic')
id = juku.add_menu_command("Mode", "Add Small Capacitor (alt+c)", mode_add_ceramic)
juku.add_accelerator('alt', 'c', id)

#=== Tools menu ===

def preferences_dialog(id):
	juku.preferences_dialog()
juku.add_menu_command("Tools", "Preferences", preferences_dialog)

def run_python_script(id):
	(ok, filepath) = juku.open_file_dialog("Run Python Script", "Python scripts (*.py)|*.py|All files (*.*)|*.*")
	if ok:
		execfile(filepath)
juku.add_menu_command("Tools", "Run Python Script", run_python_script)

def create_rats_nest(id):
	juku.create_rats_nest(False)
id = juku.add_menu_command("Tools", "Create Rat's Nest (alt+n)", create_rats_nest)
juku.add_accelerator('alt', 'n', id)

def create_inverse_rats_nest(id):
	juku.create_inverse_rats_nest(False)
id = juku.add_menu_command("Tools", "Create BadConn Rat's Nest (alt+a)", create_inverse_rats_nest)
juku.add_accelerator('alt', 'a', id)

def collect_overboard_components(id):
	juku.collect_overboard_components(False)
id = juku.add_menu_command("Tools", "Return Off-Board Components", collect_overboard_components)

def check_automatic_rats_nest(id, state):
	state = bool(state)
	print "m_automatic_rats_nest = "+str(state)
	juku.set_setting("m_automatic_rats_nest", str(state))
id = juku.add_menu_check("Tools", "Automatic Rats Nest", check_automatic_rats_nest, True)

#=== Help menu ===

def about(id):
	#print "python callback: id="+str(id)
	juku.msgbox_info(juku.get_about_string(), "About jukuMagic")
id = juku.add_menu_command("Help", "About", about)
#juku.add_accelerator('none', 'WXK_HOME', id)

